using System.Collections.Generic;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;

namespace Framework.Fsm
{
    public class FsmGraphView : GraphView
    {
        private FsmGraphContext _context;
        private FsmEntryPointNode _entry;
        private FsmSearchActionProvider _actionSearch;
        private FsmData _data;
        private List<FsmStateNode> _stateNodeList;
        public FsmGraphView(FsmGraphContext context)
        {
            _context = context;
            _context.View = this;
            _context.ActionType = typeof(FsmStateAction);
            _context.ActionSearchProvider = ScriptableObject.CreateInstance<FsmSearchActionProvider>();
            _context.ActionSearchProvider.Initialize(_context);

            styleSheets.Add(Resources.Load<StyleSheet>("FsmGraph"));


            var grid = new GridBackground();
            Insert(0, grid);
            grid.StretchToParentSize();
            _entry = new FsmEntryPointNode();
            _entry.Initialize(_context);
            AddElement(_entry);
            var toolbar = new Toolbar();
            toolbar.Add(new Button(() =>
            {
                save();
            })
            { text = "保存" });
            toolbar.Add(new Button(() =>
            {
                if (_data != null)
                {
                    EditorGUIUtility.PingObject(_data);
                }
            })
            { text = "显示" });
            Add(toolbar);
            graphViewChanged = OnGraphChange;
            RegisterCallback<MouseDownEvent>(MouseDownCallback);
            RegisterCallback<KeyDownEvent>(KeyDownCallback);
            this.StretchToParentSize();

            _stateNodeList = new List<FsmStateNode>();

            this.AddManipulator(new ContentDragger());
            this.AddManipulator(new SelectionDragger());
            this.AddManipulator(new RectangleSelector());
            this.AddManipulator(new FreehandSelector());
            SetupZoom(ContentZoomer.DefaultMinScale, ContentZoomer.DefaultMaxScale);
        }
        protected virtual void KeyDownCallback(KeyDownEvent e)
        {
            if (e.keyCode == KeyCode.S && e.ctrlKey)
            {
                save();
                e.StopPropagation();
            }
        }

        void MouseDownCallback(MouseDownEvent e)
        {
            foreach (var item in _stateNodeList)
            {
                item.MouseDownCallback(e);
            }

        }
        public override List<Port> GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter)
        {
            var compatiblePorts = new List<Port>();
            var startPortView = startPort;

            ports.ForEach((port) =>
            {
                var portView = port;
                if (startPortView != portView && startPortView.node != portView.node && startPortView.direction != portView.direction)
                    compatiblePorts.Add(port);
            });

            return compatiblePorts;
        }
        public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
        {
            if (!(evt.target is FsmGraphView))
            {
                return;
            }
            Vector2 position = (evt.currentTarget as VisualElement).ChangeCoordinatesTo(contentViewContainer, evt.localMousePosition);
            _context.NodePosition = position;
            evt.menu.InsertAction(0, "添加状态节点", (e) =>
            {
                var state = new FsmStateNode();
                addStateNode(state);
            }, DropdownMenuAction.AlwaysEnabled);
        }
        public void RemovePort(Port port)
        {
            var list = this.edges.ToList();
            foreach (var item in list)
            {
                if (item.input == port)
                {
                    if (item.output != null)
                    {
                        item.output.Disconnect(item);
                    }
                    port.Disconnect(item);
                    this.RemoveElement(item);
                }
                if (item.output == port)
                {
                    if (item.input != null)
                    {
                        item.input.Disconnect(item);
                    }
                    item.output.Disconnect(item);
                    this.RemoveElement(item);
                }
            }
        }
        private GraphViewChange OnGraphChange(GraphViewChange change)
        {
            if (change.elementsToRemove != null)
            {
                foreach (GraphElement element in change.elementsToRemove)
                {
                    if (typeof(Edge).IsInstanceOfType(element) == true)
                    {

                    }
                    else if (typeof(Node).IsInstanceOfType(element) == true)
                    {
                        if (element is FsmStateNode state)
                        {
                            removeStateNode(state);
                        }
                    }
                }
            }

            if (change.movedElements != null)
            {

            }
            return change;
        }
        private void addStateNode(FsmStateNode value)
        {
            value.Initialize(_context);
            this.AddElement(value);
            _stateNodeList.Add(value);
        }
        private void removeStateNode(FsmStateNode value)
        {
            foreach (var item in value.EventList)
            {
                RemovePort(item.Port);
            }
            _stateNodeList.Remove(value);
        }
        public void Open(FsmData value)
        {
            _stateNodeList.Clear();
            foreach (var item in this.edges.ToList())
            {
                this.Remove(item);
            }
            _data = value;
            Rect pos;
            var states = new List<FsmStateNode>();
            int count = _data.States.Count;
            for (int i = 0; i < count; i++)
            {
                var item = _data.States[i];
                var state = new FsmStateNode();
                states.Add(state);
                addStateNode(state);

                state.SetName(item.Name);
                pos = state.GetPosition();
                pos.position = new Vector2(item.x, item.y);
                state.SetPosition(pos);
            }
            foreach (var item in _data.Actions)
            {
                var state = states[item.State];
                state.AddAction(item);
            }
            foreach (var item in _data.Transitions)
            {
                var name = _data.Events[item.EventId];
                var state = states[item.From];
                var e = state.AddEvent();
                e.SetName(name);
                var connect = states[item.To];
                var edge = connect.ConnectTo(e);
                this.AddElement(edge);
            }
            _entry.SetActionType(_data.Type);
            pos = _entry.GetPosition();
            pos.position = new Vector2(_data.Entry.x, _data.Entry.y);
            _entry.SetPosition(pos);

            if (states.Count > 0)
            {
                var entry = states[_data.Entry.EntryPoint];
                this.AddElement(entry.ConnectTo(_entry));
            }
            viewTransform.position = new Vector3(_data.x, _data.y, 0);
            viewTransform.scale = new Vector3(_data.zoom, _data.zoom, 1f);
        }
        private void save()
        {
            if (_data == null) return;
            var states = new List<FsmUserData>();
            Dictionary<string, bool> stateName = new Dictionary<string, bool>();
            var list = this.edges.ToList();
            foreach (var edge in list)
            {
                var i = edge.input;
                var o = edge.output;
                if (i == null || o == null) continue;
                var uo = o.userData as FsmUserData;
                var ui = i.userData as FsmUserData;
                uo.Connect = ui;
                ui.Connect = uo;
                if (ui.DataType == UserDataType.State)
                {
                    if (!states.Contains(ui))
                    {
                        states.Add(ui);
                        var node = ui.Target as FsmStateNode;
                        if (stateName.ContainsKey(node.Name))
                        {
                            if (EditorUtility.DisplayDialog("警告", "状态节点不能重名 " + node.Name, "确定", "取消"))
                            {
                                node.selected = true;
                                return;
                            }
                        }
                        else
                        {
                            stateName.Add(node.Name, true);
                        }
                    }
                }
            }
            int entry = states.IndexOf(_entry.Data.Connect);
            if (entry < 0)
            {
                return;
            }
            _data.Clear();
            _data.x = ((int)this.viewTransform.position.x);
            _data.y = ((int)this.viewTransform.position.y);
            _data.zoom = this.scale;
            var data = _data;
            data.Type = _context.ActionType.ToString();
            int index = 0;
            var elist = new List<FsmEventItem>();
            foreach (var item in states)
            {
                index = data.States.Count;
                var node = item.Target as FsmStateNode;
                FsmDataStateData d = new FsmDataStateData() { Name = node.Name };
                d.x = ((int)node.GetPosition().xMin);
                d.y = ((int)node.GetPosition().yMin);
                data.States.Add(d);
                var actions = node.GetActionItems();
                foreach (var action in actions)
                {
                    var act = new FsmDataActionData();
                    act.State = index;
                    act.Type = action.Type.ToString();
                    act.Name = action.title;
                    data.Actions.Add(act);
                    act.Params = action.GetParam();
                }
                foreach (var e in node.EventList)
                {
                    if (!e.Enabled) continue;
                    if (e.Data.Connect == null) continue;
                    elist.Add(e);
                    if (!data.Events.Contains(e.EventName))
                    {
                        data.Events.Add(e.EventName);
                    }
                }
            }
            foreach (var e in elist)
            {
                var t = new FsmTransitonData();
                t.EventId = data.Events.IndexOf(e.EventName);
                t.From = states.IndexOf(e.Node.Data);
                t.To = states.IndexOf(e.Data.Connect);
                data.Transitions.Add(t);
            }
            var pos = _entry.GetPosition();
            _data.Entry.EntryPoint = entry;
            _data.Entry.x = ((int)pos.position.x);
            _data.Entry.y = ((int)pos.position.y);
            EditorUtility.SetDirty(_data);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
            // var json = Utility.Json.ToJson(data);
            // Debug.LogError(json);
        }
    }
}